/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package driver;

import core.CalculationException;
import java.util.Arrays;
import util.*;

/**
 *
 * @author Niki
 */
public class AnalysisHelper {
    private DataSetListItem item;
    private double avgHeurPercentOfGlobObjective;
    private double KorrelationHeurToGlobObjective;
    private double avgHeurPercentOfGlobEfficiency;
    private double KorrelationHeurToGlobEfficiency;
    private double avgHeurPercentOfGlobProfileDistance;
    private double KorrelationHeurToGlobProfileDistance;
    private double alphaMaxDevianceHeurToGlobObjective;
    private int dmuMaxDevianceHeurToGlobObjective;
    private double maxDevianceHeurToGlobObjective;
    private double alphaMinDevianceHeurToGlobObjective;
    private int dmuMinDevianceHeurToGlobObjective;
    private double minDevianceHeurToGlobObjective;

    public AnalysisHelper(DataSetListItem item){
        this.item = item;
    }
    public static String GetObjectHeader(){
        String x = "Dataset;Number of DMUs;Number of outputs;Avg. number of transitions in heuristic bisection;Avg. calculation time for heur Optimization;";
        x += "Avg. calculation time for global Optimization;% objective;% pd";
        return x;
    }
    public String GetObjectToCSVString() throws CalculationException{
        this.calculate();
        
        String x = item.datasetname + ";";
        x += this.getCntDmus() + ";";
        x += this.getCntOutputVariables() + ";";
        x += this.getAvgTransitionsHeur() + ";";
        x += this.getAvgCalcTimeHeur() + ";";
        x += this.getAvgCalcTimeGlob() + ";";
        x += this.avgHeurPercentOfGlobObjective + ";";
        //x += this.KorrelationHeurToGlobObjective + ";";
        //x += this.avgHeurPercentOfGlobEfficiency + ";";
        //x += this.KorrelationHeurToGlobEfficiency + ";";
        x += this.avgHeurPercentOfGlobProfileDistance + ";";
        //x += this.KorrelationHeurToGlobProfileDistance + ";";
        return x;
    }
    public String GetExtremeDmuString(){
        String x = ";DMU;MinAlpha;MaxAlpha;AlphaDeviance;Deviance";
        x += System.getProperty("line.separator");
        x += ";" + this.dmuMaxDevianceHeurToGlobObjective + ";";
        x += this.item.dmus.get(this.dmuMaxDevianceHeurToGlobObjective).anaList.get(0).heurOptimization.getAlpha() + ";";
        x += this.item.dmus.get(this.dmuMaxDevianceHeurToGlobObjective).anaList.get(this.item.dmus.get(this.dmuMaxDevianceHeurToGlobObjective).anaList.size()-1).heurOptimization.getAlpha() + ";";
        x += this.alphaMaxDevianceHeurToGlobObjective + ";";
        x += this.maxDevianceHeurToGlobObjective + ";";
        x += System.getProperty("line.separator");
        x += ";" + this.dmuMinDevianceHeurToGlobObjective + ";";
        x += this.item.dmus.get(this.dmuMinDevianceHeurToGlobObjective).anaList.get(0).heurOptimization.getAlpha() + ";";
        x += this.item.dmus.get(this.dmuMinDevianceHeurToGlobObjective).anaList.get(this.item.dmus.get(this.dmuMinDevianceHeurToGlobObjective).anaList.size()-1).heurOptimization.getAlpha() + ";";
        x += this.alphaMinDevianceHeurToGlobObjective + ";";
        x += this.minDevianceHeurToGlobObjective + ";";
        return x;
    }
    public int getCntOutputVariables() throws CalculationException{
        return item.dmus.get(0).anaList.get(0).globOptimization.getOptimizationOutputWeights().length;
    }
    public int getCntDmus(){
        return item.dmus.size();
    }
    public double getAvgTransitionsHeur(){
        int transCnt = 0;
        for(int i = 0; i<item.dmus.size();i++){
            transCnt += item.dmus.get(i).anaList.size();
        }
        return transCnt/item.dmus.size();
    }
    public long getAvgCalcTimeHeur() throws CalculationException{
        long timeCnt = 0;
        for(int i = 0; i<item.dmus.size();i++){
            long currCnt = 0;
            for(int z=0;z<item.dmus.get(i).anaList.size();z++){
                currCnt += item.dmus.get(i).anaList.get(z).heurOptimization.getOptimizationTime();
            }
            timeCnt += currCnt/item.dmus.get(i).anaList.size();
        }
        return timeCnt/item.dmus.size();
    }
    public long getAvgCalcTimeGlob() throws CalculationException{
        long timeCnt = 0;
        for(int i = 0; i<item.dmus.size();i++){
            long currCnt = 0;
            for(int z=0;z<item.dmus.get(i).anaList.size();z++){
                currCnt += item.dmus.get(i).anaList.get(z).globOptimization.getOptimizationTime();
            }
            timeCnt += currCnt/item.dmus.get(i).anaList.size();
        }
        return timeCnt/item.dmus.size();
    }
    public void calcDevianceHeurToGlobObjective() throws CalculationException{
        this.maxDevianceHeurToGlobObjective = 100;
        this.minDevianceHeurToGlobObjective = 0;
        for(int i = 0; i<item.dmus.size();i++){
                 for(int z=0;z<item.dmus.get(i).anaList.size();z++){
                     double tempVal = item.dmus.get(i).anaList.get(z).heurOptimization.getOptimizationObjective()/item.dmus.get(i).anaList.get(z).globOptimization.getOptimizationObjective()*100;
                     if(tempVal <= this.maxDevianceHeurToGlobObjective){
                         this.maxDevianceHeurToGlobObjective = tempVal;
                         this.alphaMaxDevianceHeurToGlobObjective = item.dmus.get(i).anaList.get(z).heurOptimization.getAlpha();
                         this.dmuMaxDevianceHeurToGlobObjective = i;
                     }
                     if(tempVal >= this.minDevianceHeurToGlobObjective){
                         this.minDevianceHeurToGlobObjective = tempVal;
                         this.alphaMinDevianceHeurToGlobObjective = item.dmus.get(i).anaList.get(z).heurOptimization.getAlpha();
                         this.dmuMinDevianceHeurToGlobObjective = i;
                     }
                 }
        }
    }
    public void calcAvgHeurPercentOfGlob() throws CalculationException{
        //calculate AVG for Global Optimisation
        double objCnt = 0;
        //double effCnt = 0;
        double pdCnt = 0;
        for(int i = 0; i<item.dmus.size();i++){
            double objCurrCnt = 0;
            //double effCurrCnt = 0;
            double pdCurrCnt = 0;
            for(int z=0;z<item.dmus.get(i).anaList.size();z++){
                objCurrCnt += item.dmus.get(i).anaList.get(z).globOptimization.getOptimizationObjective();
                //Util.print(item.dmus.get(i).anaList.get(z).globOptimization.getOptimizationObjective());
                //effCurrCnt += item.dmus.get(i).anaList.get(z).globOptimization.getOptimizationObjective();
                pdCurrCnt += item.dmus.get(i).anaList.get(z).globOptimization.getProfileDistance();
            }
            objCnt += objCurrCnt/item.dmus.get(i).anaList.size();
            //effCnt += effCurrCnt/item.dmus.get(i).anaList.size();
            pdCnt += pdCurrCnt/item.dmus.get(i).anaList.size();
        }
        double globObjAvg = objCnt/item.dmus.size();
        //double globEffAvg = effCnt/item.dmus.size();
        double globPdAvg = pdCnt/item.dmus.size();

        //Util.print("---now heur");

        //calculate AVG for heuristic Optimisation
        objCnt = 0;
        //effCnt = 0;
        pdCnt = 0;
        for(int i = 0; i<item.dmus.size();i++){
            double objCurrCnt = 0;
            //double effCurrCnt = 0;
            double pdCurrCnt = 0;
            for(int z=0;z<item.dmus.get(i).anaList.size();z++){
                objCurrCnt += item.dmus.get(i).anaList.get(z).heurOptimization.getOptimizationObjective();
                //Util.print(item.dmus.get(i).anaList.get(z).heurOptimization.getOptimizationObjective());
                //effCurrCnt += item.dmus.get(i).anaList.get(z).heurOptimization.getOptimizationObjective();
                pdCurrCnt += item.dmus.get(i).anaList.get(z).heurOptimization.getProfileDistance();
            }
            objCnt += objCurrCnt/item.dmus.get(i).anaList.size();
            //effCnt += effCurrCnt/item.dmus.get(i).anaList.size();
            pdCnt += pdCurrCnt/item.dmus.get(i).anaList.size();
        }
        double heurObjAvg = objCnt/item.dmus.size();
        //double heurEffAvg = effCnt/item.dmus.size();
        double heurPdAvg = pdCnt/item.dmus.size();

        this.avgHeurPercentOfGlobObjective =  heurObjAvg/globObjAvg * 100;
        //this.avgHeurPercentOfGlobEfficiency =  heurEffAvg/globEffAvg * 100;
        this.avgHeurPercentOfGlobProfileDistance =  heurPdAvg/globPdAvg * 100;
    }
    private void calculate() throws CalculationException{
        calcAvgHeurPercentOfGlob();
        calcKorrelationHeurToGlob();
        calcDevianceHeurToGlobObjective();
    }
    public void calcKorrelationHeurToGlob() throws CalculationException{
        double ObjCorr = 0;
        //double EffCorr = 0;
        double PDCorr = 0;
        for(int i = 0; i<item.dmus.size();i++){
            double[] globObjVals = new double[item.dmus.get(i).anaList.size()];
            double[] heurObjVals = new double[item.dmus.get(i).anaList.size()];
            //double[] globEffVals = new double[item.dmus.get(i).anaList.size()];
            //double[] heurEffVals = new double[item.dmus.get(i).anaList.size()];
            double[] globPDVals = new double[item.dmus.get(i).anaList.size()];
            double[] heurPDVals = new double[item.dmus.get(i).anaList.size()];
            double[] alphaVals = new double[item.dmus.get(i).anaList.size()];
            for(int z=0;z<item.dmus.get(i).anaList.size();z++){
                globObjVals[z] = item.dmus.get(i).anaList.get(z).globOptimization.getOptimizationObjective();
                heurObjVals[z] = item.dmus.get(i).anaList.get(z).heurOptimization.getOptimizationObjective();
                alphaVals[z] = item.dmus.get(i).anaList.get(z).heurOptimization.getAlpha();
                //globEffVals[z] = item.dmus.get(i).anaList.get(z).globOptimization.getOptimizationObjective();
                //heurEffVals[z] = item.dmus.get(i).anaList.get(z).heurOptimization.getOptimizationObjective();
                globPDVals[z] = item.dmus.get(i).anaList.get(z).globOptimization.getProfileDistance();
                heurPDVals[z] = item.dmus.get(i).anaList.get(z).heurOptimization.getProfileDistance();
            }
            double tCorr = this.getPearsonCorrelation(globObjVals, heurObjVals);
            ObjCorr+= tCorr;
//            if(i==16)
//            Util.print("---" + i);
//            Util.print("Alpha: " + Arrays.toString(alphaVals));
//            Util.print("Heur: " + Arrays.toString(heurObjVals));
//            Util.print("PD: " + Arrays.toString(heurPDVals));
//            Util.print(tCorr);
            //EffCorr+= this.getPearsonCorrelation(globEffVals, heurEffVals);
            PDCorr+= this.getPearsonCorrelation(globPDVals, heurPDVals);
            //Util.print(Arrays.toString(globPDVals));
            //Util.print(Arrays.toString(heurPDVals));
        }
        this.KorrelationHeurToGlobObjective = ObjCorr/item.dmus.size();
        //this.KorrelationHeurToGlobEfficiency = EffCorr/item.dmus.size();
        this.KorrelationHeurToGlobProfileDistance = PDCorr/item.dmus.size();
    }
    static double getPearsonCorrelation(double[] scores1,double[] scores2){
        double result = 0;
        double sum_sq_x = 0;
        double sum_sq_y = 0;
        double sum_coproduct = 0;
        double mean_x = scores1[0];
        double mean_y = scores2[0];
        for(int i=2;i<scores1.length+1;i+=1){
            double sweep =Double.valueOf(i-1)/i;
            double delta_x = scores1[i-1]-mean_x;
            double delta_y = scores2[i-1]-mean_y;
            sum_sq_x += delta_x * delta_x * sweep;
            sum_sq_y += delta_y * delta_y * sweep;
            sum_coproduct += delta_x * delta_y * sweep;
            mean_x += delta_x / i;
            mean_y += delta_y / i;
        }
        double pop_sd_x = (double) Math.sqrt(sum_sq_x/scores1.length);
        double pop_sd_y = (double) Math.sqrt(sum_sq_y/scores1.length);
        double cov_x_y = sum_coproduct / scores1.length;
        result = cov_x_y / (pop_sd_x*pop_sd_y);
        return result;
    }
    public static double getCorrelation(Number[] data1, Number[] data2) {
     if (data1 == null) {
         throw new IllegalArgumentException("Null 'data1' argument.");
     }
     if (data2 == null) {
         throw new IllegalArgumentException("Null 'data2' argument.");
     }
     if (data1.length != data2.length) {
         throw new IllegalArgumentException(
             "'data1' and 'data2' arrays must have same length."
         );
     }
     int n = data1.length;
     double sumX = 0.0;
     double sumY = 0.0;
     double sumX2 = 0.0;
     double sumY2 = 0.0;
     double sumXY = 0.0;
     for (int i = 0; i < n; i++) {
         double x = 0.0;
         if (data1[i] != null) {
             x = data1[i].doubleValue();
         }
         double y = 0.0;
         if (data2[i] != null) {
             y = data2[i].doubleValue();
         }
         sumX = sumX + x;
         sumY = sumY + y;
         sumXY = sumXY + (x * y);
         sumX2 = sumX2 + (x * x);
         sumY2 = sumY2 + (y * y);
     }
     return (n * sumXY - sumX * sumY) / Math.pow((n * sumX2 - sumX * sumX)
             * (n * sumY2 - sumY * sumY), 0.5);
 }

}
